package com.thero.core.command;

import com.thero.core.chess.Room;
import com.thero.core.command.cmd.ClientRegister;
import com.thero.core.net.ClientChannel;
import com.thero.core.net.ProtocolMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import io.netty.channel.Channel;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * Created by wusk on 2017/11/28.
 *  命令表，可以根据命令字找到命令字对应的命令处理器。
 *  这个类的实例是在线程间共享的，所以需考虑线程安全问题
 */
public class CommandHandler {
    private static final Logger logger = LoggerFactory.getLogger(CommandHandler.class);

    private Map<Short, HandlerMethod> handlers;
    private Map<Integer, Room> rooms;
    private Map<Short, ClientChannel> clients;
    private ThreadPoolExecutor pool;
    private RivalMatcher matcher;
    private boolean run = true;

    public CommandHandler(){
        handlers = new HashMap<>();
        rooms = new ConcurrentHashMap<>();
        clients = new ConcurrentHashMap<>(4);
        matcher = new RivalMatcher();
        HandlerScanner scanner = new HandlerScanner();
        scanner.scan(handlers);
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
        pool = new ThreadPoolExecutor(1, 4, 10, TimeUnit.MINUTES, queue);
        heartbeat();
    }

    public void register(Channel c, short home){
        ClientChannel channel = new ClientChannel(c);
        this.clients.put(home, channel);
    }

    public void handle(ProtocolMessage message){
        short cmdCode = message.getHeader().getCommand();
        HandlerMethod handler = handlers.get(cmdCode);
        if(null == handler){
            logger.error("Got no handler with command code = {}", cmdCode);
            return;
        }

        Task task = new Task(this, message, handler);
        pool.execute(task);
    }

    public Room getRoom(int id){
        return rooms.get(id);
    }

    public void putRoom(Room room){
        rooms.put(room.getId(), room);
    }

    public ClientChannel getChannel(short id){
        return clients.get(id);
    }

    public RivalMatcher getMatcher(){
        return matcher;
    }

    private void heartbeat(){
        while (run){
            heartThread();
        }
    }

    private void heartThread(){
        new Thread(() -> {
            try{
                Thread.sleep(60000);
                Set<Short> set = clients.keySet();
                for(Short key: set){
                    ClientChannel channel = clients.get(key);
                }
            } catch (InterruptedException e) {
                logger.error("", e);
            }
        }).start();
    }
}


class Task implements Runnable{
    private static final Logger logger = LoggerFactory.getLogger(Task.class);

    private CommandHandler context;

    private ProtocolMessage message;

    private HandlerMethod handler;

    public Task(CommandHandler context, ProtocolMessage message, HandlerMethod h){
        this.context = context;
        this.message = message;
        this.handler = h;
    }

    @Override
    public void run() {
        Class type = handler.getClazz();
        Object[] parameters = new Object[2];
        parameters[0] = context;
        parameters[1] = message;
        try{
            handler.getMethod().invoke(type.newInstance(), parameters);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            logger.error("Error occurred when execute method. class = {}, method = {}", type, handler.getMethod().getName());
            logger.error("", e);
        } catch (InstantiationException e) {
            logger.error("Error occurred when execute method. class = {}, method = {}", type, handler.getMethod().getName());
            logger.error("", e);
        } catch (InvocationTargetException e) {
            logger.error("Error occurred when execute method. class = {}, method = {}", type, handler.getMethod().getName());
            logger.error("", e);
        }
    }
}
